package de.XPortLib;

import de.XPortLib.Exceptions.DataTransmissionException;
import de.XPortLib.Exceptions.TCPCommunicationException;

class CommandExecutor {
	
	/** Number of I/O pins available on WiPort */
	public static final int NUMCPINS = 3;

	/** Number of Relais on B-Module */
	public static final int NUMRELAIS = 7;

	/** instance of a Communication class object */
	private Communication XPort;
	
	/** the remote IP address of the XPort */
	protected String mIpAddress;
	
	/** the remote port of the XPort */
	protected int mPort;
	
	/** default constructor for initialization */
	public CommandExecutor() {
		XPort = new Communication();
	}

	/**
	 * @return the mIpAddress
	 */
	public String getIpAddress() {
		return mIpAddress;
	}

	/**
	 * @param ipAddress the mIpAddress to set
	 */
	public void setIpAddress(String ipAddress) {
		mIpAddress = ipAddress;
	}

	/**
	 * @return the mPort
	 */
	public int getPort() {
		return mPort;
	}

	/**
	 * @param port the mPort to set
	 */
	public void setPort(int port) {
		mPort = port;
	}
	
	/** 
	 * connects to the XPort using the previously set ipaddress and port. 
	 * 
	 * @throws TCPCommunicationException
	 * 			on communication errors
	 * */
//	public void connect() throws TCPCommunicationException {
//		XPort.connect(mIpAddress, mPort);
//	}
	
	/** disconnects from the XPort 
	 * 
	 * @throws TCPCommunicationException
	 * 			on communication errors
	 * */
	public void disconnect() {
		try {
			XPort.disconnect();
		}
		catch (TCPCommunicationException ex) { }
	}

	/**
	 * method to execute a command and receive the answer.
	 * Does also perform connect / reconnect
	 * @param command
	 * 			Command object to be executed
	 * @throws Exception
	 * @throws IOException
	 */
	public void executeCommand(Command command) throws TCPCommunicationException, DataTransmissionException {
	
		try {
			executeCommandConnected(command);
		}
		catch (TCPCommunicationException ex) {
			throw ex;
		}
		catch (DataTransmissionException ex) {
			throw ex;
		}
		finally {
			try {
				XPort.disconnect();
			} catch (Exception ex) {}
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) { }
		}
	}
	

	/**
	 * method to execute a command and receive the answer.
	 * Does NOT perform connect / reconnect
	 * @param command
	 * 			Command object to be executed
	 * @throws Exception
	 * @throws IOException
	 */
	public void executeCommandConnected(Command command) throws TCPCommunicationException, DataTransmissionException {
		boolean success = false;
		int retries = 1;

		/* Connect to XPort and send command */
		if (!XPort.connected){
			XPort.connect(mIpAddress, mPort);
		}
		
		//retry to transmit several times due to random failures
		do {
			try {
				transmitCommand(command);
				receiveAnswer(command);
				success = true;
			} catch (DataTransmissionException ex) {
	    		if (retries++ > 4) throw ex;
			}
		} while (!success);
	}

	
	/**
	 * executes a command object
	 * @param command
	 * 			Command object to execute
	 * @throws Exception
	 * @throws IOException
	 */
	private void transmitCommand(Command command) throws TCPCommunicationException {
		XPort.send(command.getFullCommand());

		/* Do we have to receive data before receiving the answer frame? */
		if (command.getPayload() != null) {
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {}
			XPort.send(command.getPayload());
		}

	}		
	
	
	/**
	 * sleep procedure to wait for data
	 * @throws Exception
	 */
	private void waitForData() {
		int timeout = 0;
		while ((!XPort.DataReady()) & (timeout < 20)) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException ex) {
			}
			timeout++;
		}		
	}
	
	
	/**
	 * receives the answer from XPort
	 * @param command
	 * 			the Command object that was executed before
	 * @throws Exception
	 * @throws IOException
	 */
	private void receiveAnswer(Command command) throws DataTransmissionException, TCPCommunicationException {
		int length;
		
		/* Do we have to receive data before receiving the answer frame? */

		if (command.getExpectedRetourSize() > 0) {
			int[] retour = new int[command.getExpectedRetourSize()];
			
			// Wait for retour
			waitForData();
			
			length = XPort.readBuffer(retour, retour.length);
			if (length != retour.length)
				throw (new DataTransmissionException("Not enough Data received"));
			else {
				command.setRetour(retour);
			}
		}
		
		// Wait for answer
		waitForData();
		int[] answer = new int[Command.CMD_LENGTH];

		length = XPort.readBuffer(answer, Command.CMD_LENGTH);


		if ((answer[0] != Command.START_CODE) | (answer[1] != command.getCommand())
				| (answer[Command.CMD_LENGTH - 1] != Command.STOP_CODE)
				| (length != Command.CMD_LENGTH)) {
			throw (new DataTransmissionException("No correct answer from B-Module"));
		}
		else {
			command.setAnswer(answer);
		}
	}

}
